package com.suning.sawp.service.impl.util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;
import javax.servlet.ServletContext;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.web.context.WebApplicationContext;

import com.alibaba.dubbo.common.utils.CollectionUtils;
import com.mongodb.WriteResult;
import com.suning.framework.dal.client.DalClient;
import com.suning.sawp.cache.RedisClient;
import com.suning.sawp.constants.ErrorCodeConstants;
import com.suning.sawp.constants.ExportConstants;
import com.suning.sawp.dto.common.ReturnMsg;
import com.suning.sawp.dto.oss.ExportTaskDto;
import com.suning.sawp.intf.util.ExportUtilsService;
import com.suning.sawp.service.util.DateUtils;

/**
 * 
 * 〈一句话功能简述〉导出工具类service <br>
 * 〈功能详细描述〉
 *
 * @author 17033093
 * @see [相关类/方法]（可选）
 * @since [产品/模块版本] （可选）
 */
@SuppressWarnings("rawtypes")
public class ExportUtilsServiceImpl implements ExportUtilsService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ExportUtilsServiceImpl.class);
    
    private WebApplicationContext webApplicationContext;

    @Resource
    DalClient dalClient;

    @Resource
    MongoTemplate mongoTemplate;

    @Autowired
    private ServletContext servletContext;

    @Autowired
    RedisClient redisClient;
    
    /**
     * 未删除
     */
    private static final int DELETE_NOT = 0;
    
    /**
     * 已删除
     */
    private static final int DELETE_YES = 1;
    
    /**
     * Mongo查询起始位置
     */
    private static int MONGO_START_INDEX = 0;
    
    /**
     * 查询条数
     */
    private static int MONGO_LIMIT = 10;
    
    /**
     * 任务正在执行
     */
    private static String TASK_STATE_RUNNING = "1";
    
    /**
     * 任务已停止
     */
    private static String TASK_STATE_STOP = "0";
    
    /**
     * 执行导出任务
     */
    @Override
    public ReturnMsg<String> executeExportTask(ExportTaskDto task) {
        ReturnMsg<String> ret = new ReturnMsg<String>();

        String className = task.getClassName();
        String methodName = task.getMethodName();
        List<Object> params = task.getParams();
        List<String> paramsType = task.getParamsType();

        // 参数校验
        if (StringUtils.isBlank(className) || StringUtils.isBlank(methodName) || null == params || null == paramsType) {
            LOGGER.error("executeExportTask. param is null. className:{}, methodName:{}, params:{}, paramsType:{}",
                    className, methodName, params, paramsType);
            ret.setError(ErrorCodeConstants.EXPORT_ERR_E001);
            return ret;
        }
        
        try {
            Object[] args = null;
            // TODO wu 无参方法调用，是否会异常？
            args = new Object[paramsType.size()];
            for (int i = 0; i < args.length; i++) {
                Class<?> obj = Class.forName(paramsType.get(i));
                if (null != params.get(i)) {
                    args[i] = obj.cast(params.get(i));
                } else {
                    args[i] = null;
                }
            }

            Class thisClass = Class.forName(className);
            Method thisMethod = null;
            Method[] methods = thisClass.getMethods();
            for (Method method : methods) {
                if (methodName.equals(method.getName())) {
                    thisMethod = method;
                    break;
                }
            }

            // 获取spring容器对象
            if (null == webApplicationContext) {
                webApplicationContext = (WebApplicationContext) servletContext
                        .getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
            }
            if (null != thisMethod) {
                Object bean = webApplicationContext.getBean(toLowerCaseFirstOne(className));
                if (null != bean) {
                    thisMethod.invoke(bean, args);
                }
            } else {
                LOGGER.error("executeExportTask. not found this method.ExportTask:{}", task);
                ret.setError(ErrorCodeConstants.EXPORT_ERR_E007);
            }
        } catch (ClassNotFoundException e) {
            LOGGER.error("executeExportTask failed.", e);
            ret.setError(ErrorCodeConstants.EXPORT_ERR_E003);
        } catch (IllegalAccessException e) {
            LOGGER.error("executeExportTask failed.", e);
            ret.setError(ErrorCodeConstants.EXPORT_ERR_E003);
        } catch (IllegalArgumentException e) {
            LOGGER.error("executeExportTask failed.", e);
            ret.setError(ErrorCodeConstants.EXPORT_ERR_E003);
        } catch (InvocationTargetException e) {
            LOGGER.error("executeExportTask failed.", e);
            ret.setError(ErrorCodeConstants.EXPORT_ERR_E003);
        } catch (Exception e) {
            LOGGER.error("executeExportTask failed.", e);
            ret.setError(ErrorCodeConstants.EXPORT_ERR_E003);
        }

        return ret;
    }

    /**
     * 添加导出任务到Mongo
     */
    @Override
    public ReturnMsg<String> addExportTask2QueueMongo(String staffId, String className, String methodName,
            List<Object> params, List<Class> paramsType) {
        ReturnMsg<String> ret = new ReturnMsg<String>();
        if (!validParamValue(staffId, className, methodName, params, paramsType)) {
            ret.setError(ErrorCodeConstants.EXPORT_ERR_E001);
            return ret;
        }
        try {
            ExportTaskDto dto = new ExportTaskDto();
            dto.setStaffId(staffId);
            dto.setClassName(className);
            dto.setMethodName(methodName);
            if (CollectionUtils.isNotEmpty(params) || CollectionUtils.isNotEmpty(paramsType)) {
                List<String> paramsTypeName = new ArrayList<String>();
                for (Class type : paramsType) {
                    paramsTypeName.add(type.getName());
                }
                dto.setParams(params);
                dto.setParamsType(paramsTypeName);
            } else {
                dto.setParams(null);
                dto.setParamsType(null);
            }
            dto.setDeleteFlag(DELETE_NOT);
            dto.setCreateTime(new Date());
            mongoTemplate.save(dto);
        } catch (Exception e) {
            LOGGER.error(
                    "addExportTask2QueueMongo failed. staffId: {}, className: {}, methodName: {}, params: {}, paramsType: {}, error: {}",
                    staffId, className, methodName, params, paramsType, e);
            ret.setError(ErrorCodeConstants.EXPORT_ERR_E999);
        }

        return ret;
    }

    /*
     * 校验参数
     */
    private static boolean validParamValue(String staffId, String className, String methodName, List<Object> params,
            List<Class> paramsType) {
        boolean validFlag = true;
        if (StringUtils.isBlank(staffId)) {
            validFlag = false;
            LOGGER.error("ExportUtilsServiceImpl validParamValue failed. staffId is null");
        }
        if (StringUtils.isBlank(className)) {
            validFlag = false;
            LOGGER.error("ExportUtilsServiceImpl validParamValue failed. className is null");
        }
        if (null == methodName) {
            validFlag = false;
            LOGGER.error("ExportUtilsServiceImpl validParamValue failed. methodName is null");
        }
        if (CollectionUtils.isNotEmpty(params) && CollectionUtils.isNotEmpty(paramsType)) {
            if (params.size() != paramsType.size()) {
                validFlag = false;
                LOGGER.error("ExportUtilsServiceImpl validParamValue failed. params`s size is not equals paramsType`s size");
            }
            for (Class type : paramsType) {
                if (null == type) {
                    validFlag = false;
                    LOGGER.error("ExportUtilsServiceImpl validParamValue failed. paramsType is null");
                    break;
                }
            }
        }
        return validFlag;
    }

    /*
     * 文件名路径解析，并获取首字母转小写
     */
    private static String toLowerCaseFirstOne(String word) {
        if (StringUtils.isBlank(word)) {
            return "";
        }
        String[] w = word.split("\\.");
        word = w[w.length - 1];
        if (Character.isLowerCase(word.charAt(0))) {
            return word;
        } else {
            return (new StringBuilder()).append(Character.toLowerCase(word.charAt(0))).append(word.substring(1))
                    .toString();
        }
    }

    /**
     * 更新导出任务删除字段状态
     */
    @Override
    public void updateTaskDelFlagMongo(ExportTaskDto task) {
        try {
            LOGGER.info("updateTaskDelFlagMongo.task:" + task);
            Query queryHorAct = new Query();
            Criteria criteria = new Criteria();
            criteria.and(ExportTaskDto.Attributes.EXPORT_TASK_ID).is(task.getId());
            queryHorAct.addCriteria(criteria);
            Update update = new Update();
            update.set(ExportTaskDto.Attributes.DELETE_FLAG, DELETE_YES);
            WriteResult rsp = mongoTemplate.updateFirst(queryHorAct, update, ExportTaskDto.class);
            LOGGER.info("updateExportTaskMongo. Rsp:{}", rsp);
        } catch (Exception e) {
            LOGGER.error("updateTaskDelFlagMongo failed.", e);
        }
    }

    /**
     * 循环执行导出任务
     */
    @Override
    public void loopInvokeExportTask() {
        LOGGER.info("loopInvokeExportTask. start");
        // 从redis中获取标志 -> 若为1标示任务正在执行，直接跳过；其他情况则执行
        String exportTaskState = redisClient.get(ExportConstants.EXPORT_LOOP_TASK_STATE);
        if (TASK_STATE_RUNNING.equals(exportTaskState)) {
            LOGGER.warn("loopInvokeExportTask. Task is Running.");
            return;
        }

        // 查询10个导出任务
        Query query = new Query();
        query.with(new Sort(new Order(Direction.ASC, ExportTaskDto.Attributes.CREATE_TIME)));
        query.skip(MONGO_START_INDEX).limit(MONGO_LIMIT);
        Criteria criteria = new Criteria();
        criteria.and(ExportTaskDto.Attributes.DELETE_FLAG).is(DELETE_NOT);
        query.addCriteria(criteria);
        List<ExportTaskDto> taskList = mongoTemplate.find(query, ExportTaskDto.class);
        if (CollectionUtils.isNotEmpty(taskList)) {
            LOGGER.info("loopInvokeExportTask. Task begin. TaskList:{}", taskList);
            // 修改redis中的值为1，任务进入执行状态
            redisClient.set(ExportConstants.EXPORT_LOOP_TASK_STATE, TASK_STATE_RUNNING);
            redisClient.expireAt(ExportConstants.EXPORT_LOOP_TASK_STATE, redisExpireTime());
            try {
                for (ExportTaskDto dto : taskList) {
                    if (null == dto) {
                        continue;
                    }
                    // 执行任务
                    ReturnMsg<String> exeTaskRet = this.executeExportTask(dto);
                    LOGGER.info("loopInvokeExportTask. exeTaskResult:{}", exeTaskRet);
                    // 不管成功失败，修改mongo删除状态
                    this.updateTaskDelFlagMongo(dto);
                }
            } catch (Exception e) {
                LOGGER.error("loopInvokeExportTask loop failed.", e);
            }
            // 修改redis中的值为0，标示任务执行结束
            redisClient.set(ExportConstants.EXPORT_LOOP_TASK_STATE, TASK_STATE_STOP);
            redisClient.expireAt(ExportConstants.EXPORT_LOOP_TASK_STATE, redisExpireTime());
            LOGGER.info("loopInvokeExportTask. Task End.");
        } else {
            LOGGER.warn("loopInvokeExportTask. TaskList is null");
        }

    }
    
    /*
     * 一天有效期
     */
    private long redisExpireTime() {
        // 取当前时间的小时数和分钟数
        Date tomorrowZeroPoint = DateUtils.getNextDay(DateUtils.getNowZeroPoint(), 1);
        return tomorrowZeroPoint.getTime() / 1000;
    }

}
